Skip to content

[New Permission 4/5] smartcontract: define topology/resource/index permission flags#3942

Merged
juan-malbeclabs merged 9 commits into
mainfrom
jo/permission-trindex-flags
Jun 30, 2026
Merged

[New Permission 4/5] smartcontract: define topology/resource/index permission flags#3942
juan-malbeclabs merged 9 commits into
mainfrom
jo/permission-trindex-flags

Conversation

@juan-malbeclabs

@juan-malbeclabs juan-malbeclabs commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Permission rollout — stacked PR series. Review/merge order:

  1. #3206 — enforce Permission-based authorization in existing instructions
  2. #3942 — define topology/resource/index permission flags (3/4) — stacked on [New Permission 3/5] smartcontract: enforce Permission-based authorization in existing instructions #3206
  3. #3943 — enforce topology/resource/index permission flags (4/4) — stacked on [New Permission 4/5] smartcontract: define topology/resource/index permission flags #3942

Retarget each PR's base to main as its upstream merges.
👉 You are here: #3942 (PR 3/4).

Summary

  • Adds three permission flags — TOPOLOGY_ADMIN (1<<15), RESOURCE_ADMIN (1<<16), INDEX_ADMIN (1<<17) — so segment-routing topologies, ResourceExtension accounts, and internal Index accounts can be delegated without granting the broad FOUNDATION flag (today they are foundation-only).
  • Maps each flag to the foundation_allowlist in authorize()'s legacy path, so authorization behavior is unchanged until a Permission account is supplied.
  • Exposes the names (topology-admin / resource-admin / index-admin) in the serviceability CLI for permission set --add / --remove, and adds matching constants to the Go, TypeScript, and Python SDKs.
  • Documents the flags in PERMISSION.md and the serviceability README.md.

This PR is definition-only; processor enforcement lands in the follow-up 4/4. Stacked on #3206.

Testing Verification

  • authorize() legacy-mapping unit tests for each new flag (allowed via a foundation member, denied for others).
  • CLI permission name↔bitmask round-trip tests cover the three new names.

Reference: smartcontract/programs/doublezero-serviceability/PERMISSION.md

@juan-malbeclabs juan-malbeclabs changed the title [New Permission 3/4] smartcontract: define topology/resource/index permission flags [New Permission 4/5] smartcontract: define topology/resource/index permission flags Jun 27, 2026
@juan-malbeclabs juan-malbeclabs force-pushed the jo/permission-enforcement branch from f9a4ff5 to 8e8e6ac Compare June 29, 2026 17:41
- Add execute_authorized_transaction_quiet to DoubleZeroClient trait and
  DZClient impl, restoring quiet mode for ban and closeaccount commands
  that was lost when switching to execute_authorized_transaction
- Restore onchain allocation support in CreateSubscribeUserCommand SDK
  command (feature-flag-gated ResourceExtension PDA logic removed in
  permission enforcement refactor)
- Restore atomic path tests and fixture resource extension PDA fields in
  create_subscribe_user_test.rs
- SDK: route execute_transaction and execute_authorized_transaction through a
  single execute_transaction_inner, restoring the protocol-max compute-budget /
  heap-frame requests, skip_preflight, and structured error handling that the
  build_and_send detour had dropped for the whole SDK (C1/H1).
- SDK: append the Permission PDA as the trailing account so it stays after
  payer+system, matching authorize()'s read order; the quiet authorized variant
  now shares the same builder (H2/H3/L3).
- SDK: memoize the Permission PDA lookup and retry it on transient RPC errors,
  removing the per-send un-retried get_account (M3).
- SDK: add DZClient-level tests asserting compute-budget presence and the
  trailing Permission account, with and without a permission account (M2).
- authorize: drop the retired activator authority from USER_ADMIN/NETWORK_ADMIN/
  MULTICAST_ADMIN legacy fallbacks; document the ACCESS_PASS_ADMIN expansion (M1).
- authorize: verify program ownership before inspecting account data (L1).
- accesspass/set: document the optional-account count invariant (M4).
- CHANGELOG: correct the SDK entry to match the unified builder.
…delete/ban

DeleteUserCommand and RequestBanUserCommand authorize the final instruction
with USER_ADMIN, but first strip the user's multicast roles through
UpdateMulticastGroupRoles, whose processor only accepted the access-pass owner
or a foundation member. A USER_ADMIN-only payer was therefore blocked on the
prerequisite cleanup.

Allow the role-update processor to accept USER_ADMIN for the removal-only case
(no roles being granted), and route UpdateMulticastGroupRolesCommand through
execute_authorized_transaction so the payer's Permission account is appended
when present. Adding roles still requires the owner or foundation.
…to-injection

The SDK auto-appends the payer's Permission PDA whenever it exists on-chain, so
a foundation member whose own Permission account was suspended, under-privileged,
or uninitialized was routed through the Some branch of authorize() and denied
the PERMISSION_ADMIN instruction needed to repair it — the None-branch lockout
fallback never ran.

Extract foundation_permission_recovery() and apply it in the Some branch too:
when the supplied Permission account does not grant the flag, a foundation
member requesting PERMISSION_ADMIN is still allowed. PDA-address and ownership
checks remain hard errors; recovery is scoped to PERMISSION_ADMIN only.
@juan-malbeclabs juan-malbeclabs force-pushed the jo/permission-enforcement branch from 20dcbed to 1ccffdb Compare June 30, 2026 13:11
…rmission flags

Add TOPOLOGY_ADMIN (1<<15), RESOURCE_ADMIN (1<<16), and INDEX_ADMIN (1<<17)
to permission_flags, with legacy authorization mapping each to the foundation
allowlist in authorize() (plus unit tests). Expose the new names in the
serviceability CLI (topology-admin/resource-admin/index-admin) and add the
matching constants to the Go, TypeScript, and Python SDKs.

Documents the flags in PERMISSION.md and the serviceability README. This
defines the permissions; enforcement in the processors lands in 4/4.
@juan-malbeclabs juan-malbeclabs force-pushed the jo/permission-trindex-flags branch from 696cfc3 to 0c9e787 Compare June 30, 2026 13:23
juan-malbeclabs added a commit that referenced this pull request Jun 30, 2026
…ation in existing instructions (#3206)

> **Permission rollout — stacked PR series.** Review/merge order:
> 1. [#3206 — enforce Permission-based authorization in existing
instructions](#3206)
> 2. [#3942 — define topology/resource/index permission flags
(3/4)](#3942) — stacked on
#3206
> 3. [#3943 — enforce topology/resource/index permission flags
(4/4)](#3943) — stacked on
#3942
>
> Retarget each PR's base to `main` as its upstream merges.
> **👉 You are here: #3206 (base of the follow-up stack).**

## Summary of Changes
- Wire `authorize()` into the privileged instruction processors that
gate on it: `accesspass/{close,set}` (`ACCESS_PASS_ADMIN`) and
`user/{delete,requestban}` (`USER_ADMIN`). Each appends the caller's
Permission PDA as an optional trailing account, read last by
`authorize()`. The user owner can still delete their own account without
a Permission account.
- Add `execute_authorized_transaction` (and its `_quiet` variant) to the
Rust SDK. They share the existing `execute_transaction_inner` builder,
so compute-budget, preflight, and error-reporting behavior are identical
to `execute_transaction`; the only difference is the optional trailing
Permission PDA, which is resolved at most once per client (retried on
transient RPC errors, then memoized).
- Switch the affected SDK commands (`accesspass/{close,set}`,
`user/{delete,requestban}`, `tenant/delete`, and the `permission/*` CRUD
commands) to `execute_authorized_transaction` so the Permission account
is included transparently.
- Drop the retired activator authority from the
`USER_ADMIN`/`NETWORK_ADMIN`/`MULTICAST_ADMIN` legacy fallbacks.
`ACCESS_PASS_ADMIN` legacy authority (foundation + sentinel + feed) is
applied uniformly across its instructions and documented in
`authorize.rs`.

## Testing Verification
- New DZClient-level tests assert the assembled instruction list carries
the protocol-max compute-budget/heap-frame requests and that the
Permission account is appended after `payer`+`system`, with and without
an on-chain permission account.
- `authorize` unit tests cover the legacy fallbacks (including the
activator now being denied for the admin roles) and the
Permission-account path (PDA, ownership, discriminator, status, and flag
checks).
- `cargo test -p doublezero_sdk` and `cargo test -p
doublezero-serviceability` pass.

Related RFC/series: the Permission account and `authorize()` mechanism
land in the earlier PRs of this series; this PR enforces them in
existing instructions.
Base automatically changed from jo/permission-enforcement to main June 30, 2026 15:14
@juan-malbeclabs juan-malbeclabs enabled auto-merge (squash) June 30, 2026 15:15
@juan-malbeclabs juan-malbeclabs merged commit 9fde9ac into main Jun 30, 2026
34 checks passed
@juan-malbeclabs juan-malbeclabs deleted the jo/permission-trindex-flags branch June 30, 2026 15:43
juan-malbeclabs added a commit that referenced this pull request Jun 30, 2026
…ermission flags (#3943)

> **Permission rollout — stacked PR series.** Review/merge order:
> 1. [#3206 — enforce Permission-based authorization in existing
instructions](#3206)
> 2. [#3942 — define topology/resource/index permission flags
(3/4)](#3942) — stacked on
#3206
> 3. [#3943 — enforce topology/resource/index permission flags
(4/4)](#3943) — stacked on
#3942
>
> Retarget each PR's base to `main` as its upstream merges.
> **👉 You are here: #3943 (PR 4/4).**

## Summary

- Replaces the direct `foundation_allowlist` checks with `authorize()`
in topology (`create` / `delete` / `clear` / `assign-node-segments`) →
`TOPOLOGY_ADMIN`, resource (`create` / `allocate` / `deallocate` /
`close`) → `RESOURCE_ADMIN`, and index (`create` / `delete`) →
`INDEX_ADMIN`.
- The `Permission` account is read as the optional trailing account; the
variable-length `clear` and `assign-node-segments` layouts detect it by
PDA match before consuming their link/device lists.
- Backward compatible: with no `Permission` account the legacy
foundation path still applies, so existing callers (controlplane,
current SDK) keep working.
- Switches the corresponding SDK commands to
`execute_authorized_transaction` so the payer's `Permission` PDA is
appended when it exists onchain.

**Behavior change:** the topology instructions now reject unauthorized
callers with `NotAllowed` (`Custom(8)`) instead of `Unauthorized`
(`Custom(22)`), since `authorize()` is the single rejection path;
affected tests updated.

Depends on the 3/4 PR (stacked).

## Testing Verification

- New end-to-end test
`test_topology_create_with_permission_account_allowed`: a non-foundation
key holding `TOPOLOGY_ADMIN` creates a topology via its `Permission`
account — exercises the new authorization path through a real processor.
- topology / index / resource / permission integration suites pass;
topology error-code assertions updated `Unauthorized` → `NotAllowed`.

Reference:
`smartcontract/programs/doublezero-serviceability/PERMISSION.md`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants